#light
namespace FSharp.WebTools.ClientControls

open System
open System.Web
open System.Web.UI
open System.Web.UI.WebControls

open Microsoft.FSharp.Quotations.Typed

open FSharp.WebTools
open FSharp.WebTools.Core
open FSharp.WebTools.ClientControls
open FSharp.WebTools.ClientLibrary

(*-----------------------------------------------------------------------------------------------*)

[<NeutralSide>]
type ListItem = 
  val mutable value : string
  val mutable text : string
  
  [<ReflectedDefinition>]
  new(key, txt) = { value=key; text=txt; }
  
  [<ReflectedDefinition>]
  member this.Value 
    with get() = this.value
    and set(v) = this.value <- v
  
  [<ReflectedDefinition>]
  member this.Text
    with get() = this.text
    and set(v) = this.text <- v
  
(*-----------------------------------------------------------------------------------------------*)
  
[<MixedSide; ParseChildren(true)>]
type DropDownList = 
  inherit ClientControl as base
  interface INamingContainer

  val mutable dataSource : ResizeArray<obj>
  val mutable clientSelChanged : ClientEvent
  
  [<DuplexField>]
  val mutable textField : string
  [<DuplexField>]
  val mutable valueField : string
  [<DuplexField>]
  val mutable selIndex : int
  
  new() = 
    let fnull() = (Array.zero_create 1).[0]
    { selIndex = 0; dataSource = null; textField = ""; valueField = ""; 
      clientSelChanged = fnull() }

  [<ReflectedDefinition>]
  member this.ClientSelectionChanged =
    client { return this.clientSelChanged }
    
  member this.GetKeyValue(o:obj) = 
    let value = o.GetType().GetProperty(this.valueField).GetValue(o, [| |]).ToString()
    let text  = o.GetType().GetProperty(this.textField).GetValue(o, [| |]).ToString()
    (value, text)

  member this.DataValueField
    with get() = this.valueField
    and set(v) = this.valueField <- v

  member this.DataTextField
    with get() = this.textField
    and set(v) = this.textField <- v
  
  [<ReflectedDefinition>]    
  member this.SelectedIndex
    with get() = duplex { return this.selIndex }
    and set(v) = duplex { do this.selIndex <- v } // todo: update view
    
  [<ReflectedDefinition>]
  member this.SelectedItem 
    with get() = 
      makeDuplex
        (server 
          { let (k,v) = this.GetKeyValue(this.dataSource.[this.selIndex])
            return new ListItem(k,v) })
        (client
          { let sel = DropDownList.GetItemJS(this, this.SelectedIndexJS)
            return new ListItem(sel.Value, sel.InnerText) })
          
  override this.OnInit(e) = 
    (this.Page :?> ClientPage).RegisterClientClass(type ListItem)
    base.OnInit(e)
    
  override this.Render(wr) =
    wr.Write("<select id=\"{0}\">", this.ClientID);
    let index = ref 0
    if (this.dataSource <> null) then 
      this.dataSource |> (Seq.untyped_iter (fun (o:obj) ->
        let sel = if (!index = this.selIndex) then " selected=\"selected\"" else ""
        let (value,text) = this.GetKeyValue(o)
        wr.Write("<option value=\"{0}\"{2}>{1}</option>", value, text, sel)
        index := (!index) + 1
       )) 
    wr.Write("</select>");

  [<ReflectedDefinition>]    
  member this.DataSource 
    with get() : DuplexMonad<Collections.IEnumerable> = 
      makeDuplex 
        (server { return (this.dataSource :> Collections.IEnumerable) })
        (client { return failwith "databinding is todo" })
    and set(v:Collections.IEnumerable) = 
      makeDuplex
        (server 
          { do  this.dataSource <- new ResizeArray<obj>(); 
                v |> Seq.untyped_iter this.dataSource.Add
                this.DataBind() })
        (client 
          { let vo = Internal.ClientCore.Cast(v)
            do! this.ClientDataBind(vo) })

  [<Mapping("createAndAddOption", MappingScope.Global, MappingType.Method)>]
  static member AddOption(inst:DropDownList) : IElement = (raise ClientSideScript)

  [<Mapping("getAndWrapOption", MappingScope.Global, MappingType.Method)>]
  static member GetItemJS (inst:DropDownList, index:int) : IElement = (raise ClientSideScript)

  [<Mapping("clearAllOptions", MappingScope.Global, MappingType.Method)>]
  static member ClearOption(inst:DropDownList) : unit = (raise ClientSideScript)

  [<Mapping("el.selectedIndex", MappingScope.Member, MappingType.Field)>]
  member x.SelectedIndexJS : int = (raise ClientSideScript)

  [<ReflectedDefinition>]    
  member this.ClientDataBind (v:obj) =
    client 
      { let ar  = Internal.ClientCore.Cast<obj[]>(v)
        do  DropDownList.ClearOption(this)
        do  Array.iter (fun o ->
              let s = Internal.ClientCore.GetMember(o, this.textField)
              let v = Internal.ClientCore.GetMember(o, this.valueField)
              let el = DropDownList.AddOption(this)
              el.Value <- v
              el.InnerText <- s ) ar }      
 
  [<WebBehavior(WebBehavior.ClientConstructor); ReflectedDefinition>]
  member this.ClientConstructor(el:#IElement) = // trick .. 'el' is needed by translator
    client
      { do this.clientSelChanged <- new ClientEvent();
        do this.clientSelChanged.AddClient(this.SelectionChanged); 
        do this.AttachHandler("change", Internal.ClientCore.CreateGenericHandler(this.clientSelChanged)) }
            
  [<ReflectedDefinition>]
  member this.Client_Load ((sender:Object),(e:ClientLibrary.EventArgs)) =
    client { return () }
  
  [<ReflectedDefinition>]  
  member this.SelectionChanged(o, e) =
    client { do this.selIndex <- this.SelectedIndexJS }
